一、分布式 ID 生成 6 大方案对比(面试速记表)
| 方案 | 全局唯一 | 有序性 | 高可用 | 典型 QPS单节点 | 缺点/注意事项 | 一句话口诀 |
|---|---|---|---|---|---|---|
| ① 数据库自增 | ✅ | 时间序 | 单点 | 1 w | 主库挂则断、写压力集中 | “小系统够用” |
| ② 数据库号段 | ✅ | 时间序 | 高 | 10 w | 号段用完需瞬时取号 | “批量取号,压力骤降” |
| ③ Redis INCR | ✅ | 时间序 | 较高 | 20 w | 宕机丢号、内存贵 | “内存快,持久化怕丢” |
| ④ 雪花算法 | ✅ | 毫秒有序 | 极高 | 100 w+ | 时钟回拨撞号 | “高性能,防回拨” |
| ⑤ Leaf-Segment(美团) | ✅ | 趋势增 | 极高 | 100 w | 依赖 DB 号段+内存缓存 | “号段+缓存双保险” |
| ⑥ UUID/GUID | ✅ | 完全乱序 | 极高 | 1000 w | 36 字节、索引裂 | “无序+大,只做日志” |
生产最常用组合:Leaf-Segment(趋势递增)或 雪花增强版(毫秒内有序)+ 异常时降级 UUID。
代码示例:号段模式(Spring Boot)
java
@Service
public class SegmentService {
@Resource private SegmentDAO dao;
private final AtomicLong cursor = new AtomicLong();
private volatile long max = 0;
@PostConstruct
public void init(){
reload();
}
// 无锁取号
public long nextId(){
long c = cursor.getAndIncrement();
if (c < max) return c;
synchronized (this) { // 号段用完重新拿
if (cursor.get() >= max) reload();
return cursor.getAndIncrement();
}
}
private void reload(){
Segment seg = dao.fetchNextSegment(); // DB 取新号段
cursor.set(seg.getBegin());
max = seg.getEnd();
}
}DB 表:segment(biz_tag, max_id, step, version) 用乐观锁防多节点同时取号。
“一致性哈希把 ‘全体重新映射’ 变成 ‘只动邻居’;
虚拟节点把 ‘物理少导致倾斜’ 变成 ‘虚拟多而均匀’, 最终让 增删节点时数据迁移量最小、负载最平滑。”